#include <stdio.h>
#include "threads/thread.h"
#include "threads/synch.h"
#include "devices/timer.h"
#include <random.h>
#include "threads/narrow-bridge.h"

#define LEFT_DIRECTION   0 // left direction
#define RIGHT_DIRECTION  1 // right direction
#define MAX_CARS_ALLOWED 3 // maximum number of allowed cars on bridge
#define EMERGENCY_PRIO   1 // emergency priority
#define RELAX_PRIO       0 // relax priority

//semaphores for checking if crossing bridge is okay from a particular direction
static struct semaphore avg_ok_to_cross_from_left;
static struct semaphore avg_ok_to_cross_from_right;
static struct semaphore em_ok_to_cross_from_left;
static struct semaphore em_ok_to_cross_from_right;

// semaphore for protecting the critical section
static struct semaphore sem_mutex;
static struct semaphore sem_global;

static int active_avg_car_left  = 0; // number of average cars coming from left which are passing the bridge
static int active_em_car_left   = 0; // number of emergency cars coming from left which are passing the bridge
static int active_avg_car_right = 0; // number of average cars coming from right which are passing the bridge
static int active_em_car_right  = 0; // number of emergency cars coming from right which are passing the bridge

static int waiting_avg_car_left  = 0; // number of waiting average cars coming from left
static int waiting_em_car_left   = 0; // number of waiting emergency cars coming from left
static int waiting_avg_car_right = 0; // number of waiting average cars coming from right
static int waiting_em_car_right  = 0; // number of waiting emergency cars coming from right

//static int total_threads         = 0; // total number of remaining threads / cars

/*
 * Returns total number of all cars passing the bridge and coming from left
 */
static int GetTotalCarFromLeft()
{
	return active_avg_car_left  + active_em_car_left;
}

/*
 * Returns total number of all cars passing the bridge and coming from right
 */
static int GetTotalCarFromRight()
{
	return active_avg_car_right + active_em_car_right;
}

/*
 * Returns total number of all waiting emergency cars
 */
static int GetTotalWaitingEmergencyCar()
{
	return waiting_em_car_left + waiting_em_car_right;
}

/*
 * Returns total number of all cars passing the bridge
 */
static int GetTotalCarsOnBridge()
{
	return GetTotalCarFromLeft() + GetTotalCarFromRight();
}

static void ArriveBridge(int direc, int prio)
{
   //Entering critical section
   sema_down(&sem_mutex);

   //If there is an incoming car from the left
   if(direc == LEFT_DIRECTION)
   {
	   //If the car from the left is an emergency car
	   if(prio == EMERGENCY_PRIO)
	   {
		  //If the total cars on the bridge doesn't exceed the MAX_CARS_ALLOWED
		  //AND there is no cars arriving on the bridge from the right
		  //AND there is no waiting emergency car from the right
		  //Then we let the emergency car from the left to cross the bridge.
		  if(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED &&
		    GetTotalCarFromRight() == 0 &&
		    waiting_em_car_right   == 0)
		  {
			  active_em_car_left++;
			  sema_up(&em_ok_to_cross_from_left);
		  }

		  //else the emergency car has to wait
		  else
		  {
			  waiting_em_car_left++;
		  }

		  //we have already gone out from the critical section
		  sema_up(&sem_mutex);
		  //blocks waiting cars until other threads releases the semaphore
		  sema_down(&em_ok_to_cross_from_left);
	  }
	  else
	  {
		  //if the number of cars on the bridge doesn't exceed the MAX_CAR_ALLOWED
		  //AND there isn´t a car coming from the right side,
		  //AND there is no emergency car waiting
		  //let the cars from left to cross the bridge.
		  if(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED  &&
		     GetTotalCarFromRight() == 0               &&
			 GetTotalWaitingEmergencyCar() == 0)
		  {
			  active_avg_car_left++;
			  sema_up(&avg_ok_to_cross_from_left);
		  }

		  //else the average car has to wait
		  else
		  {
			  waiting_avg_car_left++;
		  }

		  //we have already gone out from the critical section
		  sema_up(&sem_mutex);
		  //blocks waiting cars until other threads releases the semaphore
		  sema_down(&avg_ok_to_cross_from_left);
	  }
   }

   //If there is an incoming car from the right
   else if(direc == RIGHT_DIRECTION)
   {
	   //If the incoming car from the right is an emergency car
	   if(prio == EMERGENCY_PRIO)
	   {
		   //If the total cars on the bridge doesn't exceed the MAX_CARS_ALLOWED
		   //AND there is no cars arriving on the bridge from the left
		   //AND there is no waiting emergency car from the left
		   //Then we let the emergency car from the right to cross the bridge.
		   if(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED  &&
			 GetTotalCarFromLeft() == 0 &&
			  waiting_em_car_left == 0)
		   {
			  active_em_car_right++;
			  sema_up(&em_ok_to_cross_from_right);
		   }

		   //else the emergency car has to wait
		   else
		   {
			  waiting_em_car_right++;
		   }

		   //we have already gone out from the critical section
		   sema_up(&sem_mutex);
		   //blocks waiting cars until other threads releases the semaphore
		   sema_down(&em_ok_to_cross_from_right);
	   }
	   else
	   {
		   //if the number of cars on the bridge doesn't exceed the MAX_CAR_ALLOWED
		   //AND there isn´t a car coming from the right side,
		   //AND there is no emergency car waiting
		   //let the cars from right to cross the bridge.
		   if(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED  &&
				   GetTotalCarFromRight() == 0 &&
				   GetTotalWaitingEmergencyCar() == 0)
		   {

			   active_avg_car_right++;
			   sema_up(&avg_ok_to_cross_from_right);
		   }

		   //Else the car has to wait
		   else
		   {
			  waiting_avg_car_right++;
		   }

		   //we have already gone out from the critical section
		   sema_up(&sem_mutex);
		   //blocks waiting cars until other threads releases the semaphore
		   sema_down(&avg_ok_to_cross_from_right);
	   }
   }
}

static void ExitBridge(int direc, int prio)
{
	//Entering critical section
	sema_down(&sem_mutex);

	//If the car crossing the bridge is from left
	if (direc == LEFT_DIRECTION)
	{
		//Indicates that the particular average car from left has successfully left the bridge
		if (prio == EMERGENCY_PRIO)
		{
			active_em_car_left--;
		}

		//Indicates that the particular emergency car from left has successfully left the bridge
		else
		{
			active_avg_car_left--;
		}

		//If there is a waiting emergency car from the left, prioritize that car first
		if (waiting_em_car_left > 0)
		{
		  waiting_em_car_left--;
		  active_em_car_left++;
		  sema_up(&em_ok_to_cross_from_left);
		}

		//If there is a waiting emergency car from right and there is no car on the bridge from the left
		else if(waiting_em_car_right > 0 && GetTotalCarsOnBridge() == 0)
		{
			//fill up the bridge until MAX_CAR_ALLOWED
			//prioritize the emergency car from the right first
			while(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED && waiting_em_car_right > 0)
			{
				waiting_em_car_right--;
				active_em_car_right++;
				sema_up(&em_ok_to_cross_from_right);
			}

			//If there is a space on the bridge and there is no waiting emergency car from right,
			//allow the waiting average car from right to cross bridge.
			while(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED && waiting_avg_car_right > 0)
			{
				waiting_avg_car_right--;
				active_avg_car_right++;
				sema_up(&avg_ok_to_cross_from_right);
			}
		}

		//If there is no waiting emergency cars anymore
		else if (GetTotalWaitingEmergencyCar() == 0)
		{
			//Since it isn't necessary to be fair, just let other waiting average car from left to pass
			if(waiting_avg_car_left > 0)
			{
				waiting_avg_car_left--;
				active_avg_car_left++;
				sema_up(&avg_ok_to_cross_from_left);
			}

			//If there is a waiting average car from the right and there is no traffic from the left
			else if(waiting_avg_car_right > 0 && GetTotalCarsOnBridge() == 0)
			{
				//fill up the bridge until MAX_CAR_ALLOWED
				while(waiting_avg_car_right > 0 && GetTotalCarsOnBridge() < MAX_CARS_ALLOWED)
				{
					waiting_avg_car_right--;
					active_avg_car_right++;
					sema_up(&avg_ok_to_cross_from_right);
				}
			}
		}
	}

	//If the car crossing from the bridge is from the right
	else
	{
		//Indicates that a particular average car from right has successfully left the bridge
		if (prio == EMERGENCY_PRIO)
		{
			active_em_car_right--;
		}
		//Indicates that a particular emergency car from right has successfully left the bridge
		else
		{
			active_avg_car_right--;
		}

		//If there is a waiting emergency car from right, let it be the next to cross
		if (waiting_em_car_right > 0)
		{
			waiting_em_car_right--;
			active_em_car_right++;
			sema_up(&em_ok_to_cross_from_right);
		}
		//If there is a waiting emergency car from left and there is no car on the bridge from the right
		else if(waiting_em_car_left > 0 && GetTotalCarsOnBridge() == 0)
		{
			//fill up the bridge until MAX_CAR_ALLOWED
			//prioritize the emergency car from the right first
			while(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED && waiting_em_car_left > 0)
			{
				waiting_em_car_left--;
				active_em_car_right++;
				sema_up(&em_ok_to_cross_from_left);
			}

			//If there is a space on the bridge and there is no waiting emergency car from left,
			//allow the waiting average car from left to cross bridge.
			while(GetTotalCarsOnBridge() < MAX_CARS_ALLOWED && waiting_avg_car_left > 0)
			{
				waiting_avg_car_left--;
				active_avg_car_left++;
				sema_up(&avg_ok_to_cross_from_left);
			}
		}
		//If there is no waiting emergency cars anymore
		else if (GetTotalWaitingEmergencyCar() == 0)
		{
			//Since it isn't necessary to be fair, just let other waiting average car from right to pass
			if(waiting_avg_car_right > 0)
			{
				waiting_avg_car_right--;
				active_avg_car_right++;
				sema_up(&avg_ok_to_cross_from_right);
			}

			//If there is a waiting average car from the left and there is no traffic from the right
			else if(waiting_avg_car_left > 0 && GetTotalCarsOnBridge() == 0)
			{
				//fill up the bridge until MAX_CAR_ALLOWED
				while(waiting_avg_car_left > 0 && GetTotalCarsOnBridge() < MAX_CARS_ALLOWED)
				{
					waiting_avg_car_left--;
					active_avg_car_left++;
					sema_up(&avg_ok_to_cross_from_left);
				}
			}
		}

	}

	//Count the remaining threads/ cars
	//total_threads--;

	//Indicates the last thread to be exiting the function, release halt
	/*if(total_threads == 0)
	{
		sema_up(&sem_global);
	}*/

	//End of critical section
	sema_up(&sem_mutex);
}

static void CrossBridge(int direc,int prio)
{
   printf("Going on the bridge -> direc: %i prio: %i\n",direc,prio);
   timer_sleep ((random_ulong () % 10) + 5);
   printf("Going out the bridge -> direc: %i prio: %i\n",direc,prio);
}

/* The process of crossing over the narrow bridge */
static void OneVehicle (int direc, int prio)
{
  ArriveBridge (direc, prio);
  CrossBridge (direc, prio);
  ExitBridge (direc, prio);
}

/*
 * Helper function for the creation of threads representing average car coming from left
 */
static void CreateAverageCarLeft(void *aux)
{
	OneVehicle(0,0);
}

/*
 * Helper function for the creation of threads emergency average car coming from left
 */
static void CreateEmergencyCarLeft(void *aux)
{
	OneVehicle(0,1);
}

/*
 * Helper function for the creation of threads representing average car coming from right
 */
static void CreateAverageCarRight(void *aux)
{
	OneVehicle(1,0);
}

/*
 * Helper function for the creation of threads representing emergency car coming from right
 */
static void CreateEmergencyCarRight(void *aux)
{
	OneVehicle(1,1);
}

/*
 * Starts execution of the narrow bridge task. This function is called within
 * the test function 'narrow_bridge'.
 *
 * param num_vehicles_left:   number of threads to be created representing the number of average cars coming from left
 * param num_vehicles_right:  number of threads to be created representing the number of average cars coming from right
 * param num_emergency_left:  number of threads to be created representing the number of emergency cars coming from left
 * param num_emergency_right: number of threads to be created representing the number of emergency cars coming from right
 */
void start_narrow_bridge(unsigned int num_vehicles_left,  unsigned int num_vehicles_right,
                         unsigned int num_emergency_left, unsigned int num_emergency_right)
{

	sema_init(&sem_mutex, 1);
	//sema_init(&sem_global, 0);
    sema_init(&avg_ok_to_cross_from_left, 0);
    sema_init(&avg_ok_to_cross_from_right, 0);
    sema_init(&em_ok_to_cross_from_left, 0);
    sema_init(&em_ok_to_cross_from_right, 0);

    //total_threads = num_vehicles_left + num_vehicles_right + num_emergency_left + num_emergency_right;

	unsigned int i;
	for(i = 0; i < num_vehicles_left; i++)
	{
		thread_create("Average Car Left ", 0, CreateAverageCarLeft, 0);
	}

	for(i = 0; i < num_vehicles_right; i++)
	{
		thread_create("Average Car Right ", 0, CreateAverageCarRight, 0);
	}

	for(i = 0; i < num_emergency_left; i++)
	{
		thread_create("Emergency Car Left ", 0, CreateEmergencyCarLeft, 0);
	}

	for(i = 0; i < num_emergency_right; i++)
	{
		thread_create("Emergency Car Right ", 0, CreateEmergencyCarRight, 0);
	}

	//Halt execution and wait for last thread to be finished
	//sema_down(&sem_global);
}
